﻿// Copyright (c) 2013, Simon Carter.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted 
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of 
//   conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of 
//  conditions and the following disclaimer in the documentation and/or other materials provided 
//  with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// History
// Initial Release - Simon Carter
//

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.IO;

using ErrorManager.Server;

namespace ErrorManager.Server.DAL
{
    /// <summary>
    /// Assignable permissions for each user
    /// </summary>
    [Flags]
    public enum UserSecurityPermissions
    {
        /// <summary>
        /// No Permissions (default)
        /// </summary>
        None = 0,

        /// <summary>
        /// User has permission to manage system users
        /// </summary>
        ManageSystemUsers = 1,

        /// <summary>
        /// User has permission to configure the server
        /// </summary>
        ConfigureServer = 2,

        /// <summary>
        /// User has permission to change own password
        /// </summary>
        ChangeOwnPassword = 4
    }

    /// <summary>
    /// Log Level - Use Debug to add enhanced logging.
    /// </summary>
    public enum EngineLoggingLevel { Normal, Debug }

    /// <summary>
    /// Base Data Access Layer
    /// </summary>
    public abstract class BaseEngine
    {
        #region Initialisation

        /// <summary>
        /// Initialisation method
        /// </summary>
        /// <param name="logLevel">Log Level</param>
        /// <param name="connectionString">Connection String used by DAL (if required)</param>
        public abstract void Initialise(EngineLoggingLevel logLevel, string connectionString);

        /// <summary>
        /// Initialised Method
        /// 
        /// Called after the DAL has been initialised
        /// </summary>
        public abstract void Initialised();

        #endregion Initialisation

        #region Service Settings

        //private static 
        /// <summary>
        /// Returns the IP address the TCP is bound to.
        /// </summary>
        /// <returns>Empty String</returns>
        public abstract string BindIPAddress();

        /// <summary>
        /// Returns the port that the TCP is bound to
        /// </summary>
        /// <returns>int Port to which the TCP will bind</returns>
        public abstract int BindIPPort();

        #endregion Service Settings

        #region User Editable Server Configuration

        /// <summary>
        /// Returns the currently configured Port for the server
        /// </summary>
        /// <param name="userName">Name of user making request</param>
        /// <returns>port number if user has permission, otherwise 0 (zero)</returns>
        public abstract int ServerConfigGetPort(string userName);

        /// <summary>
        /// Sets the port number used by the service
        /// </summary>
        /// <param name="userName">Name of user making request</param>
        /// <param name="port">New port number</param>
        /// <returns>void</returns>
        public abstract void ServerConfigSetPort(string userName, int port);

        /// <summary>
        /// Returns the currently configured DAL for the server
        /// </summary>
        /// <param name="userName">Name of user making request</param>
        /// <returns>DAL name if user has permissions, otherwise blank string</returns>
        public abstract string ServerConfigGetDAL(string userName);

        /// <summary>
        /// Set's the active Data Access Layer (DAL)
        /// </summary>
        /// <param name="userName">Name of user making the request</param>
        /// <param name="dataAccessLayer">New DAL Class Name</param>
        public abstract void ServerConfigSetDAL(string userName, string dataAccessLayer);

        #endregion User Editable Server Configuration

        #region Client Options

        /// <summary>
        /// Logs a client into the system
        /// </summary>
        /// <param name="userName">Username trying to login</param>
        /// <param name="password">Password </param>
        /// <returns></returns>
        public abstract bool ClientLogin(string userName, string password);

        /// <summary>
        /// Determines whether a client from specific IP address can connect
        /// </summary>
        /// <param name="ipAddress">IP Address for client</param>
        /// <returns>bool, true if can connect, otherwise false</returns>
        public abstract bool ClientAllowConnect(string ipAddress);

        /// <summary>
        /// Returns the number of error reports held for the user
        /// </summary>
        /// <param name="userName"></param>
        /// <returns>Number of error reports</returns>
        public abstract uint ClientErrorReportCount(string userName);

        /// <summary>
        /// Retrieves a unique string representing the new error
        /// </summary>
        /// <param name="userName">Name of user logging an error</param>
        /// <returns>Unique string for user</returns>
        public abstract Int64 ClientErrorUniqueID(string userName);

        /// <summary>
        /// Returns a list of error numbers held on file
        /// 
        /// List is seperated by $
        /// </summary>
        /// <param name="userName">User making the request</param>
        /// <returns>string of error numbers</returns>
        public abstract string ClientErrorsGet(string userName);

        #endregion client Options

        #region User Management

        /// <summary>
        /// Determines if a user has a specific security permission
        /// </summary>
        /// <param name="currentUser">Username</param>
        /// <param name="permissions">Permissions type</param>
        /// <returns>returns true if the user has the permission, otherwise false</returns>
        public abstract bool UserHasPermission(string currentUser, UserSecurityPermissions permissions);

        /// <summary>
        /// Retrieves all users on the system
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <returns>string list of users seperated by \r char</returns>
        public abstract string UsersGet(string currentUser);

        /// <summary>
        /// Creates a new user on the system
        /// </summary>
        /// <param name="userName">Name of new user</param>
        public abstract bool UserCreate(string currentUser, string userName);

        /// <summary>
        /// Set's the password for a user
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <param name="userName">User who's password is to be changed</param>
        /// <param name="password">New password for user</param>
        public abstract bool UserSetPassword(string currentUser, string userName, string password);

        /// <summary>
        /// Set the Permissions for a user
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <param name="userName">User who's Permission level will be changed</param>
        /// <param name="permissions">Permissions to assign to use</param>
        public abstract bool UserSetPermissions(string currentUser, string userName, UserSecurityPermissions permissions);

        /// <summary>
        /// Changes active state of user
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <param name="userName">User who's active state is to be changed</param>
        /// <param name="active">bool, true if user is active (able to login etc) or false (unable to use system)</param>
        public abstract bool UserSetActive(string currentUser, string userName, bool active);

        /// <summary>
        /// Gets a value indicating wether the user is active
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <param name="userName">User who's active state is to be changed</param>
        /// <returns>true if active, otherwise false</returns>
        public abstract bool UserGetActive(string currentUser, string userName);

        /// <summary>
        /// Get's a users password
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <param name="userName">User who's active state is to be changed</param>
        /// <returns>Password for user</returns>
        public abstract string UserGetPassword(string currentUser, string userName);

        /// <summary>
        /// Retrieves the last login date/time for a user
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <param name="userName">User who's active state is to be changed</param>
        /// <returns>Last Login Date/Time</returns>
        public abstract string UserGetLastLogin(string currentUser, string userName);

        /// <summary>
        /// Retrieves the permissions for a user
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <param name="userName">User who's active state is to be changed</param>
        /// <returns>List of permissions</returns>
        public abstract string UserGetPermissions(string currentUser, string userName);

        /// <summary>
        /// Returns Error information if available
        /// </summary>
        /// <param name="userName">User making the request</param>
        /// <param name="error">Error Number</param>
        /// <returns>Returns the error information</returns>
        public abstract string UserGetErrorInformation(string userName, uint error);

        /// <summary>
        /// Returns Extra Error information if available
        /// </summary>
        /// <param name="userName">User making the request</param>
        /// <param name="error">Error Number</param>
        /// <returns>Returns the extra error information</returns>
        public abstract string UserGetErrorExtraInformation(string userName, uint error);

        /// <summary>
        /// Determines whether an error also has an image
        /// </summary>
        /// <param name="userName">User making the request</param>
        /// <param name="error">Error Number</param>
        /// <returns>true if an image exists, otherwise false</returns>
        public abstract bool UserGetErrorHasImage(string userName, uint error);

        /// <summary>
        /// Returns a file name to the error string
        /// </summary>
        /// <param name="userName">User making the request</param>
        /// <param name="error">Error Number</param>
        /// <returns>File name of error image</returns>
        public abstract string UserGetErrorImage(string userName, uint error);

        /// <summary>
        /// Deletes an error from the system
        /// </summary>
        /// <param name="userName">User making the request</param>
        /// <param name="error">Error Number</param>
        public abstract void UserErrorDelete(string userName, uint error);

        /// <summary>
        /// Ignore's an error from the list, hides it from user view
        /// </summary>
        /// <param name="userName">User making the request</param>
        /// <param name="error">Error Number</param>
        public abstract void UserErrorIgnore(string userName, uint error);

        /// <summary>
        /// Returns a comma seperated list of all ignored errors
        /// </summary>
        /// <param name="userName">User making the request</param>
        /// <param name="defaultValue">default value to return if empty/not found</param>
        /// <returns>Comma seperated string list of errors to ignore</returns>
        public abstract string UserErrorGetIgnoreList(string userName, string defaultValue = ",");

        /// <summary>
        /// Delete's a user
        /// </summary>
        /// <param name="currentUser">User making the request</param>
        /// <param name="userName">User to be deleted</param>
        /// <returns>true if user deleted and all files removed, otherwise false</returns>
        public abstract bool UserDelete(string currentUser, string userName);

        #endregion User Management
    }

    /// <summary>
    /// Helper class to manage Data Access Layer
    /// </summary>
    internal class EngineHelper
    {
        private static BaseEngine _engine;
        private static string _path;

        /// <summary>
        /// Engine class 
        /// </summary>
        /// <returns>BaseEngine</returns>
        internal static BaseEngine GetEngine()
        {
            if (_engine == null)
            {
                try
                {
                    string EngineType = XML.GetXMLValue("Service", "DataAccessLayer", "ErrorServer.DAL.XML");

                    if (String.IsNullOrEmpty(EngineType))
                        EngineType = "ErrorServer.DAL.XML";

                    Type trp = Type.GetType(EngineType);
                    BaseEngine dc = (BaseEngine)Activator.CreateInstance(trp);

                    _engine = dc;

                    string LogLevel = XML.GetXMLValue("Service", "LoggingLevel");
                    
                    _engine.Initialise(LogLevel == "Normal" ? EngineLoggingLevel.Normal : EngineLoggingLevel.Debug,
                        XML.GetXMLValue("Service", "ConnectionString", ""));
                }
                catch (Exception err)
                {
                    AddToLog(err.Message);
                    AddToLog(err.StackTrace);
                    throw;
                }
            }

            return (_engine);
        }

        /// <summary>
        /// Adds an entry to the log file
        /// </summary>
        /// <param name="logText">text to be logged</param>
        internal static void AddToLog(string logText)
        {
            StreamWriter w = File.AppendText(Path + String.Format("\\Logs\\{0}.txt", DateTime.Now.ToString("ddMMyyyy")));
            try
            {
                w.WriteLine("{0},{1}", DateTime.Now.ToString("hh:mm:ss"), logText);
            }
            finally
            {
                w.Flush();
                w.Close();
                w = null;
            }
        }

        /// <summary>
        /// Returns the current path for the service
        /// </summary>
        internal static string Path
        {
            get
            {
                _path = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
                _path = System.IO.Path.GetDirectoryName(_path);
                _path = _path.Substring(6);

                return (_path);
            }
        }
    }
}
